package 多线程初阶1;
//synchronized关键字
/**
 * synchronized 原意为同步,抽象且存在歧义.理解成"互斥"更合适
 * 作用一:起到互斥的效果
 * 如果两个线程同时并发的尝试调用这个synchronized修饰的方法.
 * 此时一个线程会先执行这个方法,另一个线程会等待,等到第一个线程执行完了之后,第二个线程才会继续执行
 *
 * 就相当于"加锁"和"解锁"
 * 进入synchronized修饰的方法,就相当于加锁.
 * 出了synchronized修饰的方法,就相当于解锁.
 * 如果当前已经是加锁的状态,其他的线程就无法执行这里的逻辑,就只能阻塞等待.
 *
 * synchronized 的功能本质上就是,把"并发"变成"串行",适当的牺牲一下速度,但是换来的结果更加准确
 *
 * synchronized 除了可以用来修饰方法之外,还可以修饰一个"代码块"
 * 此时需要显示的在()中指定一个要加锁的对象
 * 如果直接修饰的非静态方法,相当于加锁的对象就是this
 */

/**
 * 啥叫"加锁的对象"?(一个奇怪的设定,,,其他语言不是这么玩的...)
 * --
 */

/**
 * synchronized
 * 特性一:起到互斥的效果
 * 特性二:还能够刷新内存(解决内存可见性问题)
 * --
 * 特性三:可重入,同一个线程连续两次针对同一个锁进行加锁操作,不会死锁
 * synchronized允许一个线程针对一把锁,咔咔连续加锁两次
 * --
 */

/**
 * synchronized修饰普通方法的话,相当于针对this进行加锁.
 * 这个时候如果两个线程并发的调用这个方法,此时是否会触发锁竞争就看实际的锁对象是否是同一个了.
 *
 * synchronized修饰静态方法的话,相当于针对类对象进行加锁.
 * 由于类对象是单例的,两个线程并发调用该方法,一定会触发锁竞争.
 */
public class TestDemo2 {
    static class Counter {
        public int count = 0;
//       synchronized public void increase(){
//            count++;
//        }
//        并发的去修改变量,可能就有问题了.但如果是串行,就莫得问题辣

        //还可以修饰一个代码块,此时需要显示的在()中指定一个要加锁的对象
//        如果直接修饰的非静态方法,相当于加锁的对象就是this
        public void increase() {
            synchronized (this) {
                count++;
            }
        }
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) {
                    counter.increase();
                    //当多个线程对同一个对象进行加锁时,就会出现互斥的特性,才能够保证线程安全
                }
            }
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) {
                    counter.increase();
                }
            }
        };
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter.count);
    }
}
